/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.compomics.fspike.contentprovider.fastaproviders.impl;

import com.compomics.dbtoolkit.toolkit.Concatenate;
import com.compomics.fspike.fastaprocessing.FastaValidator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;

/**
 *
 * @author Kenneth
 */
public class UniprotFastaProvider extends DefaultFastaProvider {

    private final String taxonomyID;
    private final File outputFolder;
    private final FastaValidator validator = new FastaValidator();

    /**
     *
     * @param taxonomyID the taxonomyID or ID's (comma separated string)
     * @param outputFolder folder where the eventual, merged fasta will be
     * stored
     */
    public UniprotFastaProvider(String taxonomyID, File outputFolder) {
        this.taxonomyID = taxonomyID;
        this.outputFolder = outputFolder;
    }

    @Override
    public File getCleanFastaFile() throws Exception {
        //CREATE A FILENAME FOR CONCATENATED FASTA FILES
        String[] taxonomy = taxonomyID.split(",");

        //create mapping species - id (for nicer file names !)
        LinkedHashMap<String, String> speciesIDMapping = new LinkedHashMap<>();
        StringBuilder cleanFastaFileName = new StringBuilder();
        for (String aTaxonomyID : taxonomy) {
            String id = aTaxonomyID.split("id:")[1].replace(")", "");
            String name = aTaxonomyID.split("\\(")[0].trim();
            speciesIDMapping.put(id, name);
            cleanFastaFileName.append(name).append("_");
        }
        //create clean file 
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        cleanFastaFileName.append(dateFormat.format(cal.getTime())).append(".fasta");
        cleanFastaFile = new File(outputFolder, cleanFastaFileName.toString());
        cleanFastaFile.createNewFile();
        //create a temp file to dump all concatenated fastas in
        File temp = new File(outputFolder, "temp.fasta");
        temp.createNewFile();
        for (String aTaxonomyID : speciesIDMapping.keySet()) {
            //downloaded fasta
            File inputFile = makeFasta(speciesIDMapping.get(aTaxonomyID));
            //merge into temp
            if (inputFile.length() > 0) {
                merge(inputFile, temp, cleanFastaFile);
                //copy the temp into the clean fasta for the next round
                FileUtils.copyFile(cleanFastaFile, temp);
            } else {
                //DO SOMETHING
            }
            inputFile.delete();
            if (dialog != null) {
                dialog.increaseSecondaryProgressCounter(1);
            }
        }
        temp.delete();
        //make a spiked version in case the user wants to keep the clean one
        spikedFasta = new File(cleanFastaFile.getParentFile(), cleanFastaFile.getName().replace(".fasta", "_spiked.fasta"));
        if (spikedFasta.exists()) {
            FileUtils.deleteQuietly(spikedFasta);
        }
        FileUtils.copyFile(cleanFastaFile, spikedFasta, true);
        validator.setFastaFile(spikedFasta);
        return cleanFastaFile;
    }

    /**
     * Wrappermethod for the dbToolkit "Concatenate" class
     *
     * @param input1 the first fasta
     * @param input2 the second fasta
     * @param outputFile the resulting fasta where all will be dumped
     */
    public void merge(File input1, File input2, File outputFile) throws IOException {
        String[] dbToolkitArgs = new String[]{
            "--input1", input1.getAbsolutePath(),
            "--input2", input2.getAbsolutePath(),
            outputFile.getAbsolutePath()};
        Concatenate.main(dbToolkitArgs);
        //check if there are duplicate entries...
        checkForDuplicates(outputFile);
    }

    private static void checkForDuplicates(File fastaFile) throws FileNotFoundException, IOException {
        BufferedReader reader = new BufferedReader(new FileReader(fastaFile));
        File tempFastaFile = new File(fastaFile.getParentFile(), "mergin.fasta");
        BufferedWriter writer = new BufferedWriter(new FileWriter(tempFastaFile));
        String line;
        StringBuilder entry = null;
        Set<String> headerMap = new HashSet<>();
        boolean write = false;
        while ((line = reader.readLine()) != null) {
            if (line.startsWith(">")) {
                if (entry != null) {
                    writer.write(entry.toString() + System.lineSeparator());
                }
                String accession = "";
                int check = line.indexOf("\\|");
                if (check != -1) {
                    accession = line.substring(line.indexOf("\\|"), line.indexOf("\\|"));
                    write = !headerMap.contains(accession);
                } else {
                    write = false;
                }
                if (write) {
                    entry = new StringBuilder(line + System.lineSeparator());
                    headerMap.add(accession);
                }
            } else if (write && entry != null) {
                entry.append(line);
            }
        }
        FileUtils.copyFile(tempFastaFile, fastaFile);
        tempFastaFile.delete();
    }

    public static boolean taxonomyHasFasta(String taxomyName) {
        try {
            final URL url = new URL("http://www.uniprot.org/uniprot/?query=organism:" + taxomyName.replace(" ", "+") + "&format=fasta&include=yes");
            HttpURLConnection huc = (HttpURLConnection) url.openConnection();
            int responseCode = huc.getResponseCode();
            // Handle response code here...
            if (responseCode >= 400) {
                return false;
            } else {
                //check the stream, just to be sure?
                InputStream inputStream = url.openStream();
                String check;
                try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream))) {
                    check = in.readLine();
                }
                if (check != null && !check.isEmpty()) {
                    return true;
                }
            }
            return false;
        } catch (IOException e) {
            return false;
        }
    }

    private File makeFasta(String taxomyName) throws Exception {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        File downloadedFastaFile = new File(outputFolder, "uniprot_" + taxomyName + "_" + dateFormat.format(cal.getTime()) + "d.fasta");
        if (!downloadedFastaFile.exists()) {
            String urlString = "http://www.uniprot.org/uniprot/?query=organism:" + taxomyName.replace(" ", "+") + "&format=fasta&include=yes";
            InputStream inputStream = new URL(urlString).openStream();
            if (dialog != null) {
                dialog.appendReport("Downloading " + downloadedFastaFile.getName(), true, false);
                //get current dialog text
            }
            transferStreams(inputStream, downloadedFastaFile);
            if (dialog != null) {
                dialog.appendReport("\t\tdone", false, true);
            }
        }
        return downloadedFastaFile;
    }

    private void transferStreams(InputStream input, File output) {
        String line;
        long downloadedBytes = 0;
        BufferedReader in = new BufferedReader(new InputStreamReader(input));
        try (BufferedWriter out = new BufferedWriter(new FileWriter(output.getAbsoluteFile()))) {
            while ((line = in.readLine()) != null) {
                if (dialog != null) {
                    dialog.setSecondaryProgressText(String.valueOf(downloadedBytes));
                }
                out.write(line + System.lineSeparator());
                out.flush();
                downloadedBytes += line.length();
            }
            out.flush();

        } catch (IOException ex) {
            Logger.getLogger(UniprotFastaProvider.class
                    .getName()).log(Level.SEVERE, null, ex);
        }
    }
}
